package com.hengx.cellview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.VelocityTracker;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;

import com.hengx.cellview.cell.Cell;

import java.util.Arrays;

public class CellView extends View {

    private int defaultWidth, defaultHeight; //默认列宽&行高
    private Paint paint; //画笔
    private Rect viewBounds; //可绘制范围
    private int[] widths, heights; //列宽&行高
    private CellMap data; //表格
    private boolean displayBorders = true; //显示边框
    private int borderColor; //边框颜色
    private boolean displayEmptyCell = false; //显示空单元格
    private int emptyCellColor; //空单元格线颜色
    private float borderWidth; //边框宽度
    private float emptyCellWidth; //空单元格线宽度
    private boolean scrollable = true; //可滚动
    private int currentCol, currentRow; //当前列&行
    private float offsetX, offsetY; //当前偏移X&Y
    private Handler refreshHandler; // 用于高频刷新的Handler
    private OnCellClickListener onCellClickListener;
    private OnCellLongClickListener onCellLongClickListener;
    private Cell focusCell; //焦点
    private boolean scrollBoundaryEnabled = false; // 默认不限制滚动
    private int maxCol, maxRow; //最大显示列数&行数
    private float minOffsetX, minOffsetY; //最小(本来是最大，因为是负数所以这里是最小)偏移距离
    private float scaleFactor = 1f; //缩放因子
    private float minScaleFactor = 0.5f; //最小缩放因子
    private float maxScaleFactor = 3f; //最大缩放因子
    private boolean scalable; //允许缩放
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        canvas.clipRect(viewBounds);
        if (data != null) {
            float drawX = (viewBounds.left + offsetX);
            for(int x = currentCol; x < data.getWidth() && drawX < viewBounds.right; x++) {
                float drawY = (viewBounds.top + offsetY);
                float width = widths[x] * scaleFactor;
                for(int y = currentRow; y < data.getHeight() && drawY < viewBounds.bottom; y++) {
                    float height = heights[y] * scaleFactor;
                    Cell cell = data.get(x, y);
                    if (cell != null) {
                        float fx = drawX;
                        float fy = drawY;
                        float fw = width;
                        float fh = height;
                        cell.setBounds(fx, fy, fx + fw, fy + fh);
                        cell.draw(paint, canvas, scaleFactor);
                        if (displayBorders) {
                            paint.setStyle(Paint.Style.STROKE);
                            paint.setColor(borderColor);
                            paint.setStrokeWidth(borderWidth);
                            canvas.drawLine(fx, fy, fx + fw, fy, paint); //→
                            canvas.drawLine(fx + fw, fy, fx + fw, fy + fh, paint); //↓
                            canvas.drawLine(fx + fw, fy + fh, fx, fy + fh, paint); //←
                            canvas.drawLine(fx, fy + fh, fx, fy, paint); //↑
                        }
                    }
                    drawY += height;
                }
                drawX += width;
            }
        }
        canvas.restore();
        /*
        绘制触摸点
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(0xff008aff);
        canvas.drawCircle(touchX, touchY, 50, paint);
        */
        /*
        这段代码用于在左上角显示坐标，调试时使用
        String text = "dx: " + downX + ", dy: " + downY + ", tx: " + touchX + ", ty: " + touchY;
        String text2 = "ox: " + offsetX + ", oy: " + offsetY;
        paint.setTextSize(30);
        paint.setColor(0xff000aff);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawText(text, 50, 50, paint);
        canvas.drawText(text2, 50, 100, paint);
        */
    }
    
    private float downX, downY, touchX, touchY;
    private float lastCol, lastRow, lastOfx, lastOfy, upOfx, upOfy;
    private int upCol, upRow;
    private Scroller mScroller;
    private ScaleGestureDetector scaleGestureDetector;
    private float velocityY, velocityX;
    private VelocityTracker mVelocityTracker; //速度追踪器
    private int MIN_FLING_DISTANCE = 50; //最小滑动距离
    private int MIN_FLING_VELOCITY = 10; //最小滑动速度
    private long time = 0;
    private boolean isLongClick = false;
    private boolean stoped = false;
    private MotionEvent longEvent;
    private boolean isClick;
    private boolean move;
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        touchX = event.getX();
        touchY = event.getY();
        if (scalable) scaleGestureDetector.onTouchEvent(event);
        if (focusCell != null) {
            Cell c = focusCell;
            if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
                focusCell = null;
            }
            if (c.onTouch(this, event)) {
                return true;
            }
        }
        switch(event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            downX = touchX;
            downY = touchY;
            downEvent(event);
            break;
        case MotionEvent.ACTION_MOVE:
            float deltaX = touchX - downX;
            float deltaY = touchY - downY;
            float moveX = lastOfx + deltaX;
            float moveY = lastOfy + deltaY;
            mVelocityTracker.addMovement(event);
            mVelocityTracker.computeCurrentVelocity(1000); //单位为像素/秒
            velocityY = mVelocityTracker.getYVelocity();
            velocityX = mVelocityTracker.getXVelocity();
            if ((Math.abs(touchX - downX) >= 2 || Math.abs(touchY - downY) >= 4))
                move = true;
            if ((Math.abs(touchX - downX) >= 6 || Math.abs(touchY - downY) >= 8))
                stoped = true;
            if (move && scrollable) move(moveX, moveY);
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            float dx = touchX - downX;
            float dy = touchY - downY;
            float mx = lastOfx + dx;
            float my = lastOfy + dy;
            upCol = currentCol;
            upRow = currentRow;
            upOfx = offsetX;
            upOfy = offsetY;
            stoped = true;
            isClick = false;
            refreshHandler.removeMessages(-1);
            if (!isLongClick) {
                if (!(isClick = (Math.abs(touchX - downX) > 2) || (Math.abs(touchY - downY) > 2))){
                    //单击
                    if (touchX >= viewBounds.left && touchY >= viewBounds.top && touchX <= viewBounds.right && touchY <= viewBounds.bottom) {
                        //在表格范围内单击
                        int x = getColumnByX(touchX);
                        int y = getRowByY(touchY);
                        if (x != -1 && y != -1) {
                            Cell cell = data.get(x, y);
                            if (onCellClickListener != null) {
                                onCellClickListener.onClick(x, y);
                            }
                        }
                    }
                }else {
                    if (scrollable)
                    if (Math.abs(dy) > MIN_FLING_DISTANCE && Math.abs(velocityY) > MIN_FLING_VELOCITY
                        || Math.abs(dx) > MIN_FLING_DISTANCE && Math.abs(velocityX) > MIN_FLING_VELOCITY)
                        mScroller.fling((int) mx, (int) my, (int) velocityX, (int) velocityY,
                            Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
                }
            }
            isLongClick = false;
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            downX = event.getX(event.getActionIndex());
            downY = event.getY(event.getActionIndex());
            break;
        case MotionEvent.ACTION_POINTER_UP:
            int pointerIndex = event.getActionIndex();
            int pointerId = event.getPointerId(pointerIndex);
            for (int i = 0; i < event.getPointerCount(); i++) {
                if (event.getPointerId(i) != pointerId) {
                    // 更新downX和downY为这个指针的位置
                    downX = event.getX(i);
                    downY = event.getY(i);
                    break;
                }
            }
            break;
        }
        return true;
    }
    
    private void downEvent(MotionEvent event) {
        if (!mScroller.isFinished()) {
            mScroller.abortAnimation();
        }
    	if (touchX >= viewBounds.left && touchY >= viewBounds.top && touchX <= viewBounds.right && touchY <= viewBounds.bottom) {
            //在表格范围内按下
            int x = getColumnByX(touchX);
            int y = getRowByY(touchY);
            if (x != -1 && y != -1 && data != null) {
                Cell cell = data.get(x, y);
                if (cell != null) {
                    focusCell = cell;
                    if (cell.onTouch(this, event)) {
                        return;
                    }
                }
            }
        }
        lastCol = currentCol;
        lastRow = currentRow;
        lastOfx = offsetX;
        lastOfy = offsetY;
        mVelocityTracker = VelocityTracker.obtain();
        mVelocityTracker.addMovement(event);
        refreshHandler.sendEmptyMessage(-1);
        isClick = true;
        move = false;
        stoped = false;
        time = System.currentTimeMillis();
        new Thread(() -> {
            while (System.currentTimeMillis() - time < 600 && !stoped)
            ;
            if (stoped)
            return;
            isLongClick = true;
            refreshHandler.post(() -> {
                if (onCellLongClickListener != null) {
                    int x = getColumnByX(touchX);
                    int y = getRowByY(touchY);
                    Cell cell = data.get(x, y);
                    if (x != -1 && y != -1) {
                        onCellLongClickListener.onLongClick(x, y);
                    }
                }
            });
        }).start();
    }
    
    private int getColumnByX(float x) {
        if (x < viewBounds.left || x > viewBounds.right) return -1;
        float cx = viewBounds.left + offsetX;
        for(int i = currentCol; i < data.getWidth() && cx < viewBounds.right; i++) {
            float width = widths[i] * scaleFactor;
            if (x >= cx && x < cx + width) {
                return i;
            }
            cx += width;
        }
        return -1;
    }
    
    private int getRowByY(float y) {
        if (y < viewBounds.top || y > viewBounds.bottom) return -1;
    	float cy = viewBounds.top + offsetY;
        for(int i = currentRow; i < data.getHeight() && cy < viewBounds.bottom; i++) {
            float height = heights[i] * scaleFactor;
            if (y >= cy && y < cy + height) {
                return i;
            }
            cy += height;
        }
        return -1;
    }
    
    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            currentCol = upCol;
            currentRow = upRow;
            offsetX = upOfx;
            offsetY = upOfy;
            move(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }
    
    private void move(float x, float y) {
        if (data != null && !data.isEmpty()) {
            float curWidth = widths[currentCol] * scaleFactor;
            float curHeight = heights[currentRow] * scaleFactor;
            while (x > 0 && currentCol > 0) {
                currentCol--;
                curWidth = widths[currentCol] * scaleFactor;
                x -= curWidth;
                lastOfx -= curWidth;
            }
            while (y > 0 && currentRow > 0) {
                currentRow--;
                curHeight = heights[currentRow] * scaleFactor;
                y -= curHeight;
                lastOfy -= curHeight;
            }
            while (x < -curWidth && currentCol + 1 < data.getWidth()) {
                x += curWidth;
                lastOfx += curWidth;
                currentCol++;
                curWidth = widths[currentCol] * scaleFactor;
            }
            while (y < -curHeight && currentRow + 1 < data.getHeight()) {
                y += curHeight;
                lastOfy += curHeight;
                currentRow++;
                curHeight = heights[currentRow] * scaleFactor;
            }
            if (scrollBoundaryEnabled) {
                currentCol = Math.min(data.getWidth() - 1, Math.max(0, currentCol));
                currentRow = Math.min(data.getHeight() - 1, Math.max(0, currentRow));
                float minX = -(widths[currentCol]) * scaleFactor;
                float minY = -(heights[currentRow]) * scaleFactor;
                offsetX = Math.max(minX, Math.min(0, x));
                offsetY = Math.max(minY, Math.min(0, y));
                /*if (currentCol == 0) {
                    if (maxCol == 0) x = Math.max(x, minX);
                    offsetX = Math.min(0, x);
                } else if (currentCol == maxCol) {
                    offsetX = Math.max(minX, x);
                } else {
                    offsetX = x;
                }
                if (currentRow == 0) {
                    if (maxRow == 0) y = Math.max(y, minY);
                    offsetY = Math.min(0, y);
                } else if (currentRow == maxRow) {
                    offsetY = Math.max(minY, y);
                } else {
                    offsetY = y;
                }*/
            } else {
                offsetX = x;
                offsetY = y;
            }
        }
    }
    
    /**
      **** →   x+
      *
      ↓
    
      y+
    **/

    public CellView(Context context) {
        super(context);
        init();
    }

    public CellView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        this.viewBounds = new Rect();
        this.paint = new Paint();
        this.widths = new int[0];
        this.heights = new int[0];
        this.defaultWidth = 150;
        this.defaultHeight = 90;
        this.borderColor = 0xff808080;
        this.emptyCellColor = 0xff909090;
        this.borderWidth = 1f;
        this.emptyCellWidth = 0.5f;
        paint.setAntiAlias(true);
        this.mScroller = new Scroller(getContext());
        this.refreshHandler = new Handler(getContext().getMainLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                if (msg.what == -1) {
                    invalidate(); // 强制重绘View
                    // 继续发送
                    refreshHandler.sendEmptyMessageDelayed(-1, 16); // 大约60Hz的刷新率
                    //postText(getContext(), "刷新");
                    return true;
                }
                return false;
            }
        });
        this.scaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
    }
    
    /**
     * 获取列宽
     * @param i 列数
     * @return 宽度(px)
     */
    public int getWidth(int i) {
        if (i < 0 || i >= widths.length) throw new IndexOutOfBoundsException("索引越界，索引=" + i + "，长度=" + widths.length);
    	return widths[i];
    }
    
    /**
     * 获取行高
     * @param i 行数
     * @return 高度(px)
     */
    public int getHeight(int i) {
        if (i < 0 || i >= heights.length) throw new IndexOutOfBoundsException("索引越界，索引=" + i + "，长度=" + heights.length);
    	return heights[i];
    }

    /**
     * 设置列宽
     * @param i 列数
     * @param width 宽度
     */
    public void setWidth(int i, int width) {
    	if (i < 0) throw new IndexOutOfBoundsException("索引越界，索引=" + i + "，长度=" + widths.length);
        if (i >= widths.length) {
            int[] newArray = Arrays.copyOf(this.widths, i + 1);
            for(int i2 = widths.length; i2 < newArray.length; i2++) {
                newArray[i2] = defaultWidth;
            }
            this.widths = newArray;
        }
    	widths[i] = width;
        if (data != null) {
            for(int y = 0; y < data.getHeight(); y++) {
                data.get(i, y).onSizeChanged(width, heights[y]);
            }
        }
    }
    
    /**
     * 设置行高
     * @param i 行数
     * @param height 高度
     */
    public void setHeight(int i, int height) {
    	if (i < 0) throw new IndexOutOfBoundsException("索引越界，索引=" + i + "，长度=" + heights.length);
        if (i >= heights.length) {
            int[] newArray = Arrays.copyOf(this.heights, i + 1);
            for(int i2 = heights.length; i2 < newArray.length; i2++) {
                newArray[i2] = defaultHeight;
            }
            this.heights = newArray;
        }
    	heights[i] = height;
        if (data != null) {
            for(int x = 0; x < data.getWidth(); x++) {
                data.get(x, i).onSizeChanged(widths[x], height);
            }
        }
    }

    /**
     * 获取表格
     * @return 表格
     */
    public CellMap getData() {
        return this.data;
    }

    /**
     * 设置表格
     * @param data 表格
     */
    public void setData(CellMap data) {
        this.data = data;
        if (data != null) {
            if (data.getWidth() > this.widths.length) {
                int[] newArray = Arrays.copyOf(this.widths, data.getWidth());
                for(int i = widths.length; i < newArray.length; i++) {
                    newArray[i] = defaultWidth;
                }
                this.widths = newArray;
            }
            if (data.getHeight() > this.heights.length) {
                int[] newArray = Arrays.copyOf(this.heights, data.getHeight());
                for(int i = heights.length; i < newArray.length; i++) {
                    newArray[i] = defaultHeight;
                }
                this.heights = newArray;
            }
            for(int x = 0; x < data.getWidth(); x++) {
                for(int y = 0; y < data.getHeight(); y++) {
                    Cell c = data.get(x, y);
                    if (c != null) {
                        c.init(this);
                        c.onSizeChanged(widths[x], heights[y]);
                    }
                }
            }
        }
    }
    
    @Override
    protected void onMeasure(int w, int h) {
        super.onMeasure(w, h);
        this.viewBounds.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int ow, int oh) {
        super.onSizeChanged(w, h, ow, oh);
        this.viewBounds.set(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - getPaddingBottom());
        invalidate();
    }
    
    @Override
    public void setPadding(int arg0, int arg1, int arg2, int arg3) {
        super.setPadding(arg0, arg1, arg2, arg3);
        this.viewBounds.set(arg0, arg1, getWidth() - arg2, getHeight() - arg3);
    }

    /**
     * 获取默认列宽
     * @return 宽度(px)
     */
    public int getDefaultWidth() {
        return this.defaultWidth;
    }

    /**
     * 设置默认列宽
     * @param defaultWidth 默认宽度(px)
     */
    public void setDefaultWidth(int defaultWidth) {
        this.defaultWidth = defaultWidth;
    }

    /**
     * 获取默认行高
     * @return 高度(px)
     */
    public int getDefaultHeight() {
        return this.defaultHeight;
    }

    /**
     * 设置默认行高
     * @param defaultHeight 默认高度(px)
     */
    public void setDefaultHeight(int defaultHeight) {
        this.defaultHeight = defaultHeight;
    }
    
    /**
     * 移动到单元格
     * @param x 列数
     * @param y 行数
     */
    public void moveTo(int x, int y) {
        if (data == null) throw new NullPointerException("表格为空");
    	if (x < 0 || x >= data.getWidth() || y < 0 || y >= data.getHeight()) throw new IndexOutOfBoundsException("超出范围，x=" + x + "，y=" + y);
        this.currentCol = x;
        this.currentRow = y;
        this.offsetX = 0;
        this.offsetY = 0;
        invalidate();
    }
    
    /**
     * 移动到单元格
     * @param x 列数
     * @param y 行数
     * @param offsetX 偏移X(px)
     * @param offsetY 偏移Y(px)
     */
    public void moveTo(int x, int y, float offsetX, float offsetY) {
        if (data == null) throw new NullPointerException("表格为空");
    	if (x < 0 || x >= data.getWidth() || y < 0 || y >= data.getHeight()) throw new IndexOutOfBoundsException("超出范围，x=" + x + "，y=" + y);
        this.currentCol = x;
        this.currentRow = y;
        /*this.offsetX = offsetX;
        this.offsetY = offsetY;*/
        this.lastOfx = this.offsetX;
        this.lastOfy = this.offsetY;
        move(offsetX, offsetY);
        invalidate();
    }
    
    /**
     * dip转px
     * @param context 上下文
     * @param value 值
     * @return 结果
     */
    public int dip2px(int value){
        return (int) (value * getResources().getDisplayMetrics().density + 0.5f);
    }

    /**
     * px转dip
     * @param context 上下文
     * @param value 值
     * @return 结果
     */
    public int px2dip(int value){
        return (int) (value / getResources().getDisplayMetrics().density + 0.5f);
    }

    /**
     * 获取显示单元格边框
     * @return 是否显示
     */
    public boolean isDisplayBorders() {
        return this.displayBorders;
    }

    /**
     * 设置显示单元格边框
     * @param displayBorders 是否显示
     */
    public void setDisplayBorders(boolean displayBorders) {
        this.displayBorders = displayBorders;
    }

    /**
     * 获取边框颜色
     * @return 颜色
     */
    public int getBorderColor() {
        return this.borderColor;
    }

    /**
     * 设置边框颜色
     * @param borderColor 边框颜色
     */
    public void setBorderColor(int borderColor) {
        this.borderColor = borderColor;
    }

    /**
     * 获取是否显示空单元格指示器(交叉线)
     * @return 是否显示
     */
    public boolean isDisplayEmptyCell() {
        return this.displayEmptyCell;
    }

    /**
     * 设置是否显示空单元格指示器(交叉线)
     * @param displayEmptyCell 是否显示
     */
    public void setDisplayEmptyCell(boolean displayEmptyCell) {
        this.displayEmptyCell = displayEmptyCell;
    }

    /**
     * 获取空单元格指示器颜色
     * @return 颜色
     */
    public int getEmptyCellColor() {
        return this.emptyCellColor;
    }

    /**
     * 设置空单元格指示器颜色
     * @param emptyCellColor 颜色
     */
    public void setEmptyCellColor(int emptyCellColor) {
        this.emptyCellColor = emptyCellColor;
    }

    /**
     * 获取边框宽度
     * @return 宽度
     */
    public float getBorderWidth() {
        return this.borderWidth;
    }

    /**
     * 设置边框宽度
     * @param borderWidth 宽度
     */
    public void setBorderWidth(float borderWidth) {
        this.borderWidth = borderWidth;
    }

    /**
     * 获取空单元格指示器宽度
     * @return 宽度
     */
    public float getEmptyCellWidth() {
        return this.emptyCellWidth;
    }

    /**
     * 设置单元格指示器宽度
     * @param emptyCellWidth 宽度
     */
    public void setEmptyCellWidth(float emptyCellWidth) {
        this.emptyCellWidth = emptyCellWidth;
    }
    
    /**
     * 获取是否可滚动
     * @return 是否可滚动
     */
    public boolean isScrollable() {
    	return this.scrollable;
    }
    
    /**
     * 设置是否可滚动
     * @param scrollable 是否可滚动
     */
    public void setScrollable(boolean scrollable) {
    	this.scrollable = scrollable;
    }
    
    /**
     * 设置单元格单击事件监听器
     * @return onCellClickListener 单击监听器
     */
    public void setOnCellClickListener(OnCellClickListener onCellClickListener) {
    	this.onCellClickListener = onCellClickListener;
    }
    
    /**
     * 设置单元格长按事件监听器
     * @reurn onCellLongClickListener 长按监听器
     */
    public void setOnCellLongClickListener(OnCellLongClickListener onCellLongClickListener) {
    	this.onCellLongClickListener = onCellLongClickListener;
    }
    
    /**
     * 获取最左边的列数(可见的)
     * @return 列数
     */
    public int getCurrentColumn() {
    	return this.currentCol;
    }
    
    /**
     * 获取最上面的行数(可见的)
     * @return 行数
     */
    public int getCurrentRow() {
    	return this.currentRow;
    }

    /**
     * 获取当前偏移x坐标
     * @return x坐标
     */
    public float getOffsetX() {
        return this.offsetX;
    }

    /**
     * 获取当前偏移y坐标
     * @return y坐标
     */
    public float getOffsetY() {
        return this.offsetY;
    }

    /**
     * 获取按下x坐标
     * @return x坐标
     */
    public float getDownX() {
        return this.downX;
    }

    /**
     * 获取按下y坐标
     * @return y坐标
     */
    public float getDownY() {
        return this.downY;
    }

    /**
     * 获取当前触摸x坐标
     * @return x坐标
     */
    public float getTouchX() {
        return this.touchX;
    }

    /**
     * 获取当前触摸y坐标
     * @return y坐标
     */
    public float getTouchY() {
        return this.touchY;
    }
    
    /**
     * 获取焦点单元格
     * @return 单元格
     */
    public Cell getFocusCell() {
    	return this.focusCell;
    }
    
    /**
    * 允许缩放。
    * @return 返回是否允许缩放。
    */
    public boolean isScalable() {
        return scalable;
    }

    /**
    * 设置是否允许缩放。
    * @param scalable 允许缩放。
    */
    public void setScalable(boolean scalable) {
        this.scalable = scalable;
    }
    
    /**
     * 获取当前的缩放因子。
     * 
     * @return 当前的缩放因子。
     */
    public float getScaleFactor() {
        return scaleFactor;
    }
    
    /**
     * 设置缩放因子，同时确保它在最小值和最大值之间。
     * 
     * @param scaleFactor 要设置的新缩放因子。
     * @throws IllegalArgumentException 如果提供的值小于最小值或大于最大值。
     */
    public void setScaleFactor(float scaleFactor) {
        if (scaleFactor < minScaleFactor || scaleFactor > maxScaleFactor) {
            throw new IllegalArgumentException("缩放因子必须小于等于最小缩放因子： " + minScaleFactor + "，且必须大于等于最大缩放因子： " + maxScaleFactor);
        }
        this.scaleFactor = scaleFactor;
        adjustScaleFactor();
    }
    
    /**
     * 获取缩放因子的最小值。
     * 
     * @return 缩放因子的最小值。
     */
    public float getMinScaleFactor() {
        return minScaleFactor;
    }
    
    /**
     * 设置缩放因子的最小值。
     * 
     * @param minScaleFactor 要设置的新最小值。
     * @throws IllegalArgumentException 如果提供的值大于当前的缩放因子最大值。
     */
    public void setMinScaleFactor(float minScaleFactor) {
        if (minScaleFactor > maxScaleFactor) {
            throw new IllegalArgumentException("最小缩放因子必须小于等于最大缩放因子");
        }
        this.minScaleFactor = minScaleFactor;
        // 同时确保当前的缩放因子在新的范围内
        adjustScaleFactor();
    }
    
    /**
     * 获取缩放因子的最大值。
     * 
     * @return 缩放因子的最大值。
     */
    public float getMaxScaleFactor() {
        return maxScaleFactor;
    }
    
    /**
     * 设置缩放因子的最大值。
     * 
     * @param maxScaleFactor 要设置的新最大值。
     * @throws IllegalArgumentException 如果提供的值小于当前的缩放因子最小值。
     */
    public void setMaxScaleFactor(float maxScaleFactor) {
        if (maxScaleFactor < minScaleFactor) {
            throw new IllegalArgumentException("最大缩放因子必须大于等于最小缩放因子");
        }
        this.maxScaleFactor = maxScaleFactor;
        // 同时确保当前的缩放因子在新的范围内
        adjustScaleFactor();
    }
    
    /**
     * 调整当前的缩放因子，确保它在新的最小值和最大值之间。
     */
    private void adjustScaleFactor() {
        if (scaleFactor < minScaleFactor) {
            scaleFactor = minScaleFactor;
        } else if (scaleFactor > maxScaleFactor) {
            scaleFactor = maxScaleFactor;
        }
    }
    
    /** 单击监听器 **/
    public interface OnCellClickListener {
        void onClick(int x, int y);
    }
    /** 长按监听器 **/
    public interface OnCellLongClickListener {
        void onLongClick(int x, int y);
    }

    /**
     * 判断是否启用滚动范围限制
     * @return 是否启用
     */
    public boolean isScrollBoundaryEnabled() {
        return this.scrollBoundaryEnabled;
    }
    
    /**
     * 设置是否启用滚动范围限制
     * @param scrollBoundaryEnabled 是否启用
     */
    public void setScrollBoundaryEnabled(boolean scrollBoundaryEnabled) {
        this.scrollBoundaryEnabled = scrollBoundaryEnabled;
    }
    
    public Paint getPaint() {
    	return this.paint;
    }
    
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            scaleFactor *= detector.getScaleFactor();
            // 限制缩放因子在一定范围内，防止过度缩放
            scaleFactor = Math.max(minScaleFactor, Math.min(scaleFactor, maxScaleFactor));
            return true;
        }
    }
    
}